/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
* or http://forgerock.org/license/CDDLv1.0.html.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at legal-notices/CDDLv1_0.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2015 ForgeRock AS.
*/
package org.forgerock.opendj.maven;
import static java.lang.String.*;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.DependencyResolutionRequiredException;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
/**
* Generate a class path suitable for the Class-Path header of a Manifest file,
* allowing to filter on included jars, using excludes/includes properties.
* <p>
* There is a single goal that generates a property given by 'classPathProperty'
* parameter, with the generated classpath as the value.
*/
@Mojo(name = "generate-manifest", defaultPhase = LifecyclePhase.VALIDATE,
requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME)
public final class GenerateManifestClassPathMojo extends AbstractMojo {
private static final int MAX_LINE_LENGTH = 72;
private static final String HEADER_CLASSPATH = "Class-Path:";
/**
* The Maven Project.
*/
@Parameter(property = "project", required = true, readonly = true)
private MavenProject project;
/**
* A property to set to the content of the generated classpath string.
*/
@Parameter(required = true)
private String classPathProperty;
/**
* List of artifacts to exclude from the classpath. Each item must be of format "groupId:artifactId".
*/
@Parameter
private List<String> excludes;
/**
* List of artifacts to include in the classpath. Each item must be of format "groupId:artifactId".
*/
@Parameter
private List<String> includes;
/**
* List of additional JARs to include in the classpath. Each item must be of format "file.jar".
*/
@Parameter
private List<String> additionalJars;
/**
* Name of product jar, e.g. "OpenDJ".
*/
@Parameter
private String productJarName;
/**
* List of supported locales, separated by a ",".
* <p>
* Example: "fr,es,de"
*/
@Parameter
private String supportedLocales;
/** {@inheritDoc} */
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
try {
String classPath = getClasspath();
getLog().info(
format("Setting the classpath property: [%s] (debug to see actual value)", classPathProperty));
getLog().debug(String.format("Setting the classpath property %s to:\n%s", classPathProperty, classPath));
project.getProperties().put(classPathProperty, classPath);
} catch (DependencyResolutionRequiredException e) {
getLog().error(
String.format("Unable to set the classpath property %s, an error occured", classPathProperty));
throw new MojoFailureException(e.getMessage(), e);
}
}
/**
* Get the classpath.
* <p>
* The returned value is conform to Manifest Header syntax, where line length must be at most 72 bytes.
*
* @return the classpath string
* @throws DependencyResolutionRequiredException
*/
private String getClasspath() throws DependencyResolutionRequiredException {
final List<String> classpathItems = getClasspathItems();
final StringBuilder classpath = new StringBuilder(HEADER_CLASSPATH);
for (String item : classpathItems) {
classpath.append(" ").append(item);
}
int index = MAX_LINE_LENGTH - 2;
while (index <= classpath.length()) {
classpath.insert(index, "\n ");
index += MAX_LINE_LENGTH - 1;
}
return classpath.toString();
}
private List<String> getClasspathItems() throws DependencyResolutionRequiredException {
final List<String> classpathItems = new ArrayList<>();
// add project dependencies
for (String artifactFile : project.getRuntimeClasspathElements()) {
final File file = new File(artifactFile);
if (file.getAbsoluteFile().isFile()) {
final Artifact artifact = findArtifactWithFile(project.getArtifacts(), file);
if (isAccepted(artifact)) {
final String artifactString = artifact.getArtifactId() + "." + artifact.getType();
classpathItems.add(artifactString);
}
}
}
// add product jars, with localized versions
Collections.sort(classpathItems);
if (productJarName != null) {
if (supportedLocales != null) {
String[] locales = supportedLocales.split(",");
for (int i = locales.length - 1; i >= 0; i--) {
classpathItems.add(0, productJarName + "_" + locales[i] + ".jar");
}
}
classpathItems.add(0, productJarName + ".jar");
}
// add additional JARs
if (additionalJars != null) {
classpathItems.addAll(additionalJars);
}
return classpathItems;
}
private boolean isAccepted(Artifact artifact) {
String artifactString = artifact.getGroupId() + ":" + artifact.getArtifactId();
if (includes != null) {
if (containsIgnoreCase(includes, artifactString)) {
return true;
}
if (!includes.isEmpty()) {
return false;
}
}
return !containsIgnoreCase(excludes, artifactString);
}
private boolean containsIgnoreCase(List<String> strings, String toFind) {
if (strings == null) {
return false;
}
for (String s : strings) {
if (toFind.equalsIgnoreCase(s)) {
return true;
}
}
return false;
}
private Artifact findArtifactWithFile(Set<Artifact> artifacts, File file) {
for (Artifact artifact : artifacts) {
if (artifact.getFile() != null
&& artifact.getFile().equals(file)) {
return artifact;
}
}
return null;
}
}